﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using Caliburn.Micro;
using Caliburn.Micro.Extensions;
using CoProjectDF.Models.Login;
using CoProjectModel;
using IdeaBlade.Application.Framework.Core.Authentication;
using IdeaBlade.Application.Framework.Core.Persistence;
using IdeaBlade.EntityModel;
using Action = System.Action;

namespace CoProjectDF.Views.Login
{
    /// <summary>
    /// View model for the LoginForm.
    /// </summary>
    public class LoginViewModel : PropertyChangedBase
    {
        public LoginViewModel()
        {
            Status = "Please log in";

            if (DesignerProperties.IsInDesignTool)
            {
                LoginInfo = new LoginInfo();
            }
            else
            {
                AuthenticationService = IoC.Get<IAuthenticationService>();
                var provider = IoC.Get<IEntityManagerProvider<CoProjectEntities>>();

                LoginInfo = new LoginInfo();
                //Used to provide field error indication in the Login Prompt.
                LoginInfo.VerifierEngine = provider.Manager.VerifierEngine;
            }
        }

        public string Status { get; set; }

        /// <summary>
        /// Current login information.
        /// </summary>
        public LoginInfo LoginInfo { get; private set; }

        public bool IsLoggingIn
        {
            get { return AuthenticationService.IsLoggedIn; }
        }

        /// <summary>
        /// Gets a value indicating whether the user can presently log in.
        /// </summary>
        public bool CanLogin
        {
            get { return !IsLoggingIn; }
        }

        /// <summary>
        ///  True if the operation can be canceled.
        /// </summary>
        public bool CanCancel
        {
            get { return CurrentOperation == null || CurrentOperation.IsCompleted; }
        }

        /// <summary>
        /// Gets or sets the current registration operation.
        /// </summary>
        private BaseOperation CurrentOperation { get; set; }

        /// <summary>
        /// Gets or Sets the AuthenticationService from the IoC container.
        /// </summary>
        private IAuthenticationService AuthenticationService { get; set; }

        /// <summary>
        /// Login the user.
        /// </summary>
        /// <param name="callback"></param>
        public void Login(Action onSuccess, Action<Exception> onFail)
        {
            if (!CanLogin) return;

            LoginCore(onSuccess, onFail).ToSequential().Execute(null);
        }

        public IEnumerable<IResult> LoginCore(Action onSuccess, Action<Exception> onFail)
        {
            //Changed to use LoginAsync from DevForce-Caliburn's AuthenticationService.
            //this then changes how failure handling is managed.
            yield return CoroutineFns.AsResult(
                () => AuthenticationService.LoginAsync(LoginInfo.ToLoginCredential(), onSuccess, onFail));

            UpdateState();
        }

        /// <summary>
        /// Cancel login processing.
        /// </summary>
        public void Cancel()
        {
            if (CanCancel)
            {
                UpdateState();
            }
        }

        public void UpdateState()
        {
            NotifyOfPropertyChange(() => IsLoggingIn);
            NotifyOfPropertyChange(() => CanLogin);
            NotifyOfPropertyChange(() => CanCancel);
        }

        /// <summary>
        /// Sets a function that returns the password.
        /// </summary>
        internal void SetPasswordAccessor(Func<string> accessor)
        {
            LoginInfo.PasswordAccessor = accessor;
        }
    }
}